import {
  Controller,
  Post,
  Put,
  Delete,
  Get,
  Body,
  Query,
  Param,
  HttpCode,
  HttpStatus,
  UnauthorizedException,
  BadRequestException,
  ConflictException,
  NotFoundException,
  ParseIntPipe,
  Req,
  Res,
} from '@nestjs/common';
import type { Request, Response } from 'express';
import { AuthService } from './auth.service';
import { Public } from '../../common/decorators';
import { CurrentUser } from '../../common/decorators/current-user.decorator';
import type { JwtPayload } from './interfaces/jwt-payload.interface';
import {
  LoginDto,
  RegisterDto,
  FindUserDto,
  UpdateUserDto,
  UserDto,
  ChangePasswordDto,
  WechatLoginDto,
  type TokenResponseDto,
} from './dto';
import type { AuthResponse } from './interfaces';

/**
 * 认证控制器
 * 处理用户登录、注册等认证相关的操作
 */
@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService) { }

  /**
   * 用户登录（支持 HttpOnly Cookie）
   */
  @Public()
  @Post('login')
  @HttpCode(HttpStatus.OK)
  async login(
    @Body() loginDto: LoginDto,
    @Req() request: Request,
    @Res({ passthrough: true }) response: Response,
  ): Promise<AuthResponse> {
    const result = await this.authService.login(
      loginDto.account,
      loginDto.password,
      request,
    );

    if (!result) {
      throw new BadRequestException('账号或密码错误');
    }

    // 将 Refresh Token 存储在 HttpOnly Cookie 中
    response.cookie('refreshToken', result.refreshToken, {
      httpOnly: true, // 防止 XSS 攻击
      secure: process.env.NODE_ENV === 'production', // 生产环境仅 HTTPS
      sameSite: 'strict', // 防止 CSRF 攻击
      maxAge: 7 * 24 * 60 * 60 * 1000, // 7 天
      path: '/auth', // 仅认证相关接口可访问
    });

    // Web 端依旧通过 Cookie 读取，小程序等不支持 Cookie 的端可从响应体获取 refreshToken
    return {
      user: result.user,
      accessToken: result.accessToken,
      refreshToken: result.refreshToken,
    };
  }

  /**
   * 用户注册
   */
  @Public()
  @Post('register')
  @HttpCode(HttpStatus.CREATED)
  async register(@Body() registerDto: RegisterDto) {
    const result = await this.authService.register(registerDto);

    if (!result) {
      throw new ConflictException('用户名、邮箱或手机号已存在');
    }

    return result;
  }

  /**
   * 微信小程序登录（支持 HttpOnly Cookie）
   */
  @Public()
  @Post('wechat-login')
  @HttpCode(HttpStatus.OK)
  async wechatLogin(
    @Body() wechatLoginDto: WechatLoginDto,
    @Req() request: Request,
    @Res({ passthrough: true }) response: Response,
  ): Promise<AuthResponse> {
    const result = await this.authService.wechatLogin(wechatLoginDto, request);

    if (!result) {
      throw new UnauthorizedException('微信登录失败');
    }

    // 将 Refresh Token 存储在 HttpOnly Cookie 中
    response.cookie('refreshToken', result.refreshToken, {
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      sameSite: 'strict',
      maxAge: 7 * 24 * 60 * 60 * 1000, // 7 天
      path: '/auth',
    });

    // Web 端依旧通过 Cookie 读取，小程序等不支持 Cookie 的端可从响应体获取 refreshToken
    return {
      user: result.user,
      accessToken: result.accessToken,
      refreshToken: result.refreshToken,
    };
  }

  /**
   * 修改密码（需要认证）
   */
  @Post('change-password')
  @HttpCode(HttpStatus.OK)
  async changePassword(
    @Body() changePasswordDto: ChangePasswordDto,
    @CurrentUser() user: JwtPayload,
    @Res({ passthrough: true }) response: Response,
  ) {
    const result = await this.authService.changePassword(
      user.sub,
      changePasswordDto.oldPassword,
      changePasswordDto.newPassword,
    );

    if (!result.success) {
      throw new UnauthorizedException(result.message);
    }

    // 清除当前的 Cookie（因为所有设备已登出）
    response.clearCookie('refreshToken', { path: '/auth' });

    return {
      message: result.message,
    };
  }

  /**
   * 刷新 Access Token（公开接口，使用 HttpOnly Cookie 中的 Refresh Token）
   * 实现 Token 无感刷新
   * 支持从 Cookie、请求头或请求体中获取 Refresh Token（兼容微信小程序）
   * @param request 请求对象
   * @param response 响应对象
   * @param body 请求体（可选，用于小程序传递 refreshToken）
   * @returns 新的 Access Token
   */
  @Public()
  @Post('refresh-token')
  @HttpCode(HttpStatus.OK)
  async refreshToken(
    @Req() request: Request,
    @Res({ passthrough: true }) response: Response,
    @Body() body?: { refreshToken?: string },
  ): Promise<{ accessToken: string; tokenType: string; expiresIn: number; refreshToken?: string }> {
    // 优先从 Cookie 中获取 Refresh Token（Web 端）
    let refreshToken = request.cookies?.['refreshToken'] as string | undefined;

    // 如果 Cookie 中没有，尝试从请求头获取（小程序端）
    if (!refreshToken) {
      refreshToken = request.headers['x-refresh-token'] as string | undefined;
    }

    // 如果请求头中也没有，尝试从请求体获取（小程序端）
    if (!refreshToken && body?.refreshToken) {
      refreshToken = body.refreshToken;
    }

    if (!refreshToken) {
      throw new UnauthorizedException('未找到 Refresh Token，请重新登录');
    }

    // 刷新 Token（实现轮换机制）
    // 使用类型断言明确返回类型，避免 TypeScript 推断为 any
    const result: TokenResponseDto = await this.authService.refreshAccessToken(
      refreshToken,
      request,
    );

    // 将新的 Refresh Token 存储在 HttpOnly Cookie 中（Web 端）
    if (result.refreshToken) {
      response.cookie('refreshToken', result.refreshToken, {
        httpOnly: true,
        secure: process.env.NODE_ENV === 'production',
        sameSite: 'strict',
        maxAge: 7 * 24 * 60 * 60 * 1000,
        path: '/auth',
      });
    }

    // 返回新的 Refresh Token（小程序端需要）
    // 如果请求头中有 X-Refresh-Token，说明是小程序端，需要返回新的 refresh token
    const isMiniProgram = request.headers['x-refresh-token'] || body?.refreshToken;
    if (isMiniProgram && result.refreshToken) {
      return {
        accessToken: result.accessToken,
        tokenType: result.tokenType || 'Bearer',
        expiresIn: result.expiresIn || 3600,
        refreshToken: result.refreshToken, // 小程序端需要返回 refresh token
      };
    }

    // 返回新的 Access Token（Web 端）
    return {
      accessToken: result.accessToken,
      tokenType: result.tokenType || 'Bearer',
      expiresIn: result.expiresIn || 3600,
    };
  }

  /**
   * 登出（废除当前的 Refresh Token）
   * @param request 请求对象
   * @param response 响应对象
   * @returns 消息提示
   */
  @Post('logout')
  @HttpCode(HttpStatus.OK)
  async logout(
    @Req() request: Request,
    @Res({ passthrough: true }) response: Response,
  ): Promise<{ message: string }> {
    const refreshToken = request.cookies?.['refreshToken'] as
      | string
      | undefined;
    console.log('refreshToken', refreshToken);
    if (refreshToken) {
      await this.authService.logout(refreshToken);
    }

    // 清除 前端的HttpOnly Cookie
    response.clearCookie('refreshToken', { path: '/auth' });

    return {
      message: '登出成功',
    };
  }

  /**
   * 登出所有设备（废除该用户的所有 Refresh Token）
   * 需要从 Access Token 中获取用户 ID
   */
  @Post('logout-all')
  @HttpCode(HttpStatus.OK)
  async logoutAllDevices(
    @Req() request: Request,
    @Res({ passthrough: true }) response: Response,
  ): Promise<{ message: string }> {
    // 从 JWT 中获取用户信息（由 JwtAuthGuard 注入）
    const user = request.user;

    if (!user?.sub) {
      throw new UnauthorizedException('未找到用户信息');
    }

    await this.authService.logoutAllDevices(user.sub);

    // 清除当前 Cookie
    response.clearCookie('refreshToken', { path: '/auth' });

    return {
      message: '已从所有设备登出',
    };
  }

  /**
   * 获取用户列表（支持多条件查询和分页）
   * @param query 查询条件
   * @returns 用户列表
   */
  @Get('users')
  @HttpCode(HttpStatus.OK)
  async getUserList(@Query() query: FindUserDto) {
    return this.authService.getUserList(query);
  }

  /**
   * 根据多种条件筛选单个用户（必须放在 user/:id 之前！）
   * @param conditions 查询条件
   * @returns 用户信息
   * 示例：
   * - GET /auth/user/find?username=zhangsan
   * - GET /auth/user/find?email=zhangsan@example.com
   * - GET /auth/user/find?phone=13800138000
   */
  @Get('user/find')
  @HttpCode(HttpStatus.OK)
  async getUserByConditions(@Query() conditions: UserDto) {
    const user = await this.authService.getUserByConditions(conditions);
    if (!user) {
      throw new NotFoundException('用户不存在');
    }
    return user;
  }

  /**
   * 根据 ID 获取单个用户信息
   * @param id 用户ID
   * @returns 用户信息
   */
  @Get('user/:id')
  @HttpCode(HttpStatus.OK)
  async getUserById(@Param('id') id: number) {
    const user = await this.authService.getUser(id);

    if (!user) {
      throw new NotFoundException(`用户 ID ${id} 不存在`);
    }

    return user;
  }

  /**
   * 更新用户信息
   * @param updateUserDto 更新用户信息
   * @returns 更新后的用户信息的消息提示
   */
  @Put('user/:id')
  @HttpCode(HttpStatus.OK)
  async updateUser(
    @Param('id', ParseIntPipe) id: number,
    @Body() updateUserDto: Omit<UpdateUserDto, 'id'>,
  ) {
    // 将路径参数的 id 和 body 数据合并
    const result = await this.authService.updateUser({ id, ...updateUserDto });

    if (typeof result === 'string') {
      throw new NotFoundException(result);
    }

    return result;
  }

  /**
   * 删除用户
   * @param id 用户id
   * @returns 删除后的用户信息的消息提示
   */
  @Delete('user/:id')
  @HttpCode(HttpStatus.OK)
  async deleteUser(@Param('id') id: number) {
    const result = await this.authService.deleteUser(id);
    return result;
  }
}
